package com.yami.shop.seckill.api.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.yami.shop.bean.app.dto.*;
import com.yami.shop.bean.app.param.SubmitSeckillOrderParam;
import com.yami.shop.bean.enums.DeliveryType;
import com.yami.shop.bean.enums.OrderType;
import com.yami.shop.bean.enums.ShopStatus;
import com.yami.shop.bean.model.Order;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.vo.UserDeliveryInfoVO;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.response.ResponseEnum;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.delivery.api.manager.DeliveryOrderManager;
import com.yami.shop.manager.SubmitOrderManager;
import com.yami.shop.manager.impl.ConfirmOrderManager;
import com.yami.shop.manager.impl.ShopCartAdapter;
import com.yami.shop.manager.impl.ShopCartItemAdapter;
import com.yami.shop.seckill.api.param.SeckillOrderParam;
import com.yami.shop.seckill.api.service.SeckillCacheManager;
import com.yami.shop.seckill.common.enums.SeckillEnum;
import com.yami.shop.seckill.common.model.Seckill;
import com.yami.shop.seckill.common.model.SeckillOrder;
import com.yami.shop.seckill.common.model.SeckillSku;
import com.yami.shop.seckill.common.service.SeckillOrderService;
import com.yami.shop.seckill.common.service.SeckillService;
import com.yami.shop.seckill.common.service.SeckillSkuService;
import com.yami.shop.security.api.util.SecurityUtils;
import com.yami.shop.service.OrderService;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.ShopCustomerService;
import com.yami.shop.service.ShopDetailService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;


/**
 * @author LGH
 * @date 2019-08-28 09:36:59
 */
@RestController
@AllArgsConstructor
@RequestMapping("/p/seckill")
@Tag(name = "秒杀订单接口")
public class SeckillOrderController {

    private final SeckillService seckillService;
    private final SeckillSkuService seckillSkuService;
    private final SeckillOrderService seckillOrderService;
    private final SeckillCacheManager seckillCacheManager;
    private final Snowflake snowflake;
    private final OrderService orderService;
    private final ShopCartAdapter shopCartAdapter;
    private final ShopCartItemAdapter shopCartItemAdapter;
    private final ConfirmOrderManager confirmOrderManager;
    private final DeliveryOrderManager deliveryOrderManager;
    private final SubmitOrderManager submitOrderManager;
    private final ProductService productService;
    private final ShopDetailService shopDetailService;
    private final ShopCustomerService shopCustomerService;

    private static final Executor EXECUTOR = new ThreadPoolExecutor(
            5,
            20,
            5L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            new ThreadFactoryBuilder()
                    .setNameFormat("seckill-redis-listener-pool-%d").build());

    @Operation(summary =  "获取秒杀订单路径")
    @GetMapping( "/orderPath")
    @Parameter(name = "prodId", description = "秒杀商品id", required = true)
    public ServerResponseEntity<String> getOrderPath(Long prodId) {
        String userId = SecurityUtils.getUser().getUserId();
        Seckill seckill = seckillService.getByProdId(prodId);
        if (seckill == null || !Objects.equals(seckill.getStatus(), SeckillEnum.NORMAL.getValue()) || seckill.getEndTime().getTime() < System.currentTimeMillis()) {
            // 秒杀活动已结束
            throw new YamiShopBindException("yami.seckill.has.ended");
        }
        if (seckill.getStartTime().getTime() > System.currentTimeMillis()) {
            // 秒杀活动未开始
            throw new YamiShopBindException("yami.seckill.not.start");
        }
        String orderPath = seckillCacheManager.createOrderPath(userId + prodId);
        return ServerResponseEntity.success(orderPath);
    }


    @Operation(summary =  "确认订单")
    @PostMapping("/{orderPath}/confirm")
    @Parameter(name = "orderPath", description = "订单路径", required = true)
    public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@PathVariable("orderPath") String orderPath, @Valid @RequestBody SeckillOrderParam seckillOrderParam) {
        String userId = SecurityUtils.getUser().getUserId();
        Long prodId = seckillOrderParam.getOrderItem().getProdId();
        seckillCacheManager.checkOrderPath(userId + prodId, orderPath);
        // 秒杀sku（来自缓存），注：千万不要用这里的库存校验
        SeckillSku seckillSku = seckillSkuService.getSeckillSkuById(seckillOrderParam.getSeckillSkuId());
        if (seckillSku == null || !Objects.equals(seckillOrderParam.getOrderItem().getSkuId(), seckillSku.getSkuId())) {
            // 购物车包含无法识别的商品
            throw new YamiShopBindException("yami.shop.cart.prod.unrecognized");
        }
        List<ShopCartItemDto> shopCartItemsDb = shopCartItemAdapter.getShopCartItem(seckillOrderParam.getOrderItem(), seckillSku.getSeckillPrice(), userId, seckillOrderParam.getAddrId());
        // 将要返回给前端的完整的订单信息
        ShopCartOrderMergerDto shopCartOrderMerger = new ShopCartOrderMergerDto();
        shopCartOrderMerger.setDvyType(seckillOrderParam.getDvyType());
        shopCartOrderMerger.setOrderType(OrderType.SECKILL);
        shopCartOrderMerger.setSeckillId(seckillSku.getSeckillId());
        shopCartOrderMerger.setSeckillSkuId(seckillSku.getSeckillSkuId());

        // 筛选过滤掉不同配送的商品
        List<ShopCartItemDto> shopCartItems = confirmOrderManager.filterShopItemsByType(shopCartOrderMerger, shopCartItemsDb);
        // 该商品不满足任何的配送方式
        if (CollectionUtil.isEmpty(shopCartItems)) {
            return ServerResponseEntity.fail(ResponseEnum.ORDER_DELIVERY_NOT_SUPPORTED, shopCartOrderMerger);
        }

        ShopCartItemDto firstShopCartItem = shopCartItems.get(0);
        // 商品类别 0.实物商品 1. 虚拟商品
        int mold = 0;
        if (shopCartItems.stream().filter(shopCartItemDto -> shopCartItemDto.getMold() == 1).count() == shopCartItems.size()) {
            // 订单项中的所有商品都为虚拟商品时，才是虚拟订单
            mold = 1;
        }
        shopCartOrderMerger.setMold(mold);
        // 是否为预售订单
        seckillOrderParam.setPreSellStatus(firstShopCartItem.getPreSellStatus());
        shopCartOrderMerger.setPreSellStatus(firstShopCartItem.getPreSellStatus());

        // 购物车
        List<ShopCartDto> shopCarts = shopCartAdapter.getShopCarts(shopCartItems);

        // 计算运费，获取用户地址，自提信息
        UserDeliveryInfoVO userDeliveryInfo = new UserDeliveryInfoVO();
        if (Objects.equals(mold, 0)) {
            userDeliveryInfo = deliveryOrderManager.calculateAndGetDeliverInfo(userId, seckillOrderParam.getAddrId(), seckillOrderParam.getDvyType(), shopCartItems);
        }

        // 当算完一遍店铺的各种满减活动时，重算一遍订单金额
        confirmOrderManager.recalculateAmountWhenFinishingCalculateShop(shopCartOrderMerger, shopCarts, userDeliveryInfo);

        // 结束平台优惠的计算之后，还要重算一遍金额
        confirmOrderManager.recalculateAmountWhenFinishingCalculatePlatform(shopCartOrderMerger);

        // 计算平台佣金
        confirmOrderManager.calculatePlatformCommission(shopCartOrderMerger);

        // 缓存计算
        confirmOrderManager.cacheCalculatedInfo(userId, shopCartOrderMerger);
        return ServerResponseEntity.success(shopCartOrderMerger);
    }

    /**
     * 购物车/立即购买  提交订单,根据店铺拆单
     */
    @PostMapping("/{orderPath}/submit")
    @Operation(summary =  "提交订单", description = "提交之后返回WebSocket路径，请求对应的WebSocket路径，开始等待响应")
    @Parameter(name = "orderPath", description = "订单路径", required = true)
    public ServerResponseEntity<OrderNumbersDto> submitOrders(@PathVariable("orderPath") String orderPath,
                                                        @Valid @RequestBody SubmitSeckillOrderParam seckillOrderParam) {
        String userId = SecurityUtils.getUser().getUserId();
        ServerResponseEntity<ShopCartOrderMergerDto> dtoEntity = submitOrderManager.checkSubmitInfo(seckillOrderParam, userId);
        if (!dtoEntity.isSuccess()) {
            return ServerResponseEntity.transform(dtoEntity);
        }
        ShopCartOrderMergerDto dto = dtoEntity.getData();
        if (dto.getUserAddr() == null && !Objects.equals(dto.getMold(),1) && Objects.equals(dto.getDvyType(), DeliveryType.EXPRESS.getValue())) {
            // 请填写收货地址
            throw new YamiShopBindException("yami.delivery.address");
        }
        ShopCartOrderDto shopCartOrderDto = dto.getShopCartOrders().get(0);
        ShopCartItemDto shopCartItemDto = shopCartOrderDto.getShopCartItemDiscounts().get(0).getShopCartItems().get(0);
        seckillCacheManager.checkOrderPath(userId + shopCartItemDto.getProdId(), orderPath);

        // 秒杀活动信息（来自缓存）
        Seckill seckill = seckillService.getSeckillById(dto.getSeckillId());
        // 判断秒杀是否已经下线
        if (Objects.equals(seckill.getStatus(), SeckillEnum.INVALID.getValue())
                || DateUtil.compare(seckill.getEndTime(), new Date()) < 0
                || Objects.equals(seckill.getStatus(), SeckillEnum.OFFLINE.getValue())) {
            // 秒杀活动已结束
            throw new YamiShopBindException("yami.seckill.has.ended");
        }
        if (DateUtil.compare(seckill.getStartTime(), new Date()) > 0) {
            // 秒杀活动未开始
            throw new YamiShopBindException("yami.seckill.not.start");
        }
        // 判断之前秒杀的商品有没有超过限制，-1表示商品不限制秒杀数量
        seckillOrderService.checkOrderProdNum(seckill, userId, shopCartItemDto.getProdCount());
        // 这里一进来就减库存，但是为了防止少卖，120秒会自己更新库存~因为缓存只有60秒
        seckillCacheManager.decrSeckillSkuStocks(dto.getSeckillSkuId(), dto.getSeckillId(), shopCartItemDto.getProdCount(),shopCartItemDto.getProdId());

        dto.setUserId(userId);
        String orderNumber = String.valueOf(snowflake.nextId());
        shopCartOrderDto.setOrderNumber(orderNumber);

        dto.setLocale(LanguageEnum.valueOf(I18nMessage.getLang()).getLanguage());
        try {
            EXECUTOR.execute(() -> {
                seckillOrderService.submit(dto);
            });
        } catch (RejectedExecutionException e) {
            // 服务器繁忙，请稍后再试
            throw new YamiShopBindException("yami.network.busy");
        }
        seckillOrderService.removeConfirmOrderCache(userId);
        // 店铺客户创建
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setShopId(dto.getShopCartOrders().get(0).getShopId());
        shopCustomerService.saveCustomerByOrders(Collections.singletonList(order));
        return ServerResponseEntity.success(new OrderNumbersDto(orderNumber));
    }

    @GetMapping("/getSeckillIdByOrderNumber")
    @Operation(summary =  "根据订单编号获取秒杀id")
    @Parameter(name = "orderNumber", description = "订单编号", required = true)
    public ServerResponseEntity<Long> getSeckillIdByOrderNumber(@RequestParam("orderNumber") String orderNumber) {
        SeckillOrder seckillOrder = seckillOrderService.getOne(new LambdaQueryWrapper<SeckillOrder>().eq(SeckillOrder::getOrderNumber, orderNumber));
        if (Objects.isNull(seckillOrder)) {
            throw new YamiShopBindException("yami.data.deleted.or.not.exist");
        }
        return ServerResponseEntity.success(seckillOrder.getSeckillId());
    }

    @GetMapping("/createOrderStatus")
    @Operation(summary =  "根据orderNumber获取订单是否创建成功", description = "订单数量")
    @Parameter(name = "orderNumber", description = "订单流水号", required = true)
    public ServerResponseEntity<Integer> getCreateOrderStatus(@RequestParam("orderNumber") String orderNumber) {
        return ServerResponseEntity.success(orderService.countByOrderNumber(orderNumber));
    }

    @GetMapping("/prod")
    @Operation(summary =  "获取秒杀商品信息", description = "根据商品id，获取当前秒杀的商品活动状态 0下线 1活动中")
    @Parameter(name = "prodId", description = "商品id", required = true)
    public ServerResponseEntity<Integer> getProdActivityStatus(@RequestParam("prodId") Long prodId) {
        // 商品信息 缓存
        Product product = productService.getProductByProdId(prodId);
        if (product == null || !Objects.equals(product.getStatus(), 1)) {
            return ServerResponseEntity.success(0);
        }

        // 秒杀活动信息 缓存
        Seckill seckill = seckillService.getSeckillByProdId(prodId);
        // 秒杀已经下线
        if (seckill == null || Objects.equals(seckill.getStatus(), 0)) {
            return ServerResponseEntity.success(0);
        }
        // 判断是否删除
        if(Objects.equals(seckill.getIsDelete(), 1)){
            return ServerResponseEntity.success(0);
        }
        // 秒杀结束时间
        if (seckill.getEndTime().getTime() < System.currentTimeMillis()) {
            return ServerResponseEntity.success(0);
        }
        // 店铺详情 缓存
        ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(product.getShopId());
        if (!Objects.equals(shopDetail.getShopStatus(), ShopStatus.OPEN.value())) {
            // 店铺不处于营业状态
            return ServerResponseEntity.success(0);
        }
        return ServerResponseEntity.success(1);
    }
}
